﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using PropertyExpression.Common;

namespace PropertyExpression.ControllableQuery.Demo
{
    public static class QExecuter
    {
        public static int Execute(this INonQuery it)
        {
            using (var connection = new SqlConnection(DALTest.ConnectionString))
            {
                connection.Open();
                using (var command = connection.CreateCommand())
                {
                    command.SetQuery(it);
                    return command.ExecuteNonQuery();
                }
            }
        }

        public static T Single<T>(this IQuery<Type<T>> it)
        {
            return it.Result().Single();
        }

        public static List<T> List<T>(this IQuery<Type<T>> it)
        {
            return it.Result().ToList();
        }

        public static IEnumerable<T> Result<T>(this IQuery<Type<T>> it)
        {
            using (var connection = new SqlConnection(DALTest.ConnectionString))
            {
                connection.Open();
                using (var command = connection.CreateCommand())
                {
                    command.SetQuery(it);
                    using (var sqlDataReader = command.ExecuteReader())
                        while (sqlDataReader.Read())
                            yield return sqlDataReader.Materialize<T>();
                }
            }
        }

        public static Paging<List<T1>, int> Result<T1>(this IQuery<Paging<T1, int?>> it)
        {
            return it.Result((List<T1> item1, List<int?> item2) =>
                new Paging<List<T1>, int>(item1, item2.Single().Value));
        }

        public static Tuple<List<T1>, List<T2>> Result<T1, T2>(this IQuery<Type<T1, T2>> it)
        {
            return it.Result((List<T1> item1, List<T2> item2) => Tuple.Create(item1, item2));
        }

        private static TResult Result<T1, T2, TResult>(this IQuery it, Func<List<T1>, List<T2>, TResult> func)
        {
            return it.Result(reader => {
                var item1 = reader.ToEnumerable<T1>().ToList();
                reader.NextResult();
                var item2 = reader.ToEnumerable<T2>().ToList();
                return func(item1, item2);
            });
        }

        public static Tuple<List<T1>, List<T2>, List<T3>> Result<T1, T2, T3>(this IQuery<Type<T1, T2, T3>> it)
        {
            return it.Result(reader => {
                var item1 = reader.ToEnumerable<T1>().ToList();
                reader.NextResult();
                var item2 = reader.ToEnumerable<T2>().ToList();
                reader.NextResult();
                var item3 = reader.ToEnumerable<T3>().ToList();
                return Tuple.Create(item1, item2, item3);
            });
        }

        private static TResult Result<TResult>(this IQuery it, Func<SqlDataReader, TResult> func)
        {
            using (var connection = new SqlConnection(DALTest.ConnectionString))
            {
                connection.Open();
                using (var command = connection.CreateCommand())
                {
                    command.SetQuery(it);
                    using (var sqlDataReader = command.ExecuteReader())
                        return func(sqlDataReader);
                }
            }
        }

        private static IEnumerable<T> ToEnumerable<T>(this SqlDataReader reader)
        {
            while (reader.Read()) 
                yield return reader.Materialize<T>();
        }
    }
}